package org.shiro.config.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.shiro.basic.properties.RedisPropriety;
import org.shiro.basic.properties.ShiroPropriety;
import org.shiro.filters.ShiroAuthenticationFilter;
import org.shiro.listener.shiro.ShiroSessionListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.*;

import static org.shiro.basic.constant.ShiroConstant.ITERATIONS;
import static org.shiro.basic.constant.ShiroConstant.SHIRO_PRINCIPAL_ID;

/**
 * Shiro框架核心配置类
 *
 * @author LiKun
 * @date 2021/11/2 9:52
 */
@Configuration
public class ShiroConfiguration {
    public static final String ANON = "anon";
    public static final String AUTHC = "authc";
    public static final RedisSerializer REDIS_KEY_SERIALIZER = new StringRedisSerializer();
    public static final RedisSerializer REDIS_VALUE_SERIALIZER = new GenericJackson2JsonRedisSerializer();

    @Resource
    private ShiroPropriety shiroPropriety;
    @Resource
    private RedisPropriety redisPropriety;
    @Resource
    private CustomizeRealm customizeRealm;
    @Resource
    private ShiroSessionListener shiroSessionListener;

    /**
     * 注册Shiro安全管理器，这是Shiro的核心
     *
     * @return 安全管理器
     */
    @Bean
    public SecurityManager securityManager(SessionManager sessionManager, CacheManager cacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(sessionManager);
        securityManager.setCacheManager(cacheManager);
        securityManager.setRealm(customizeRealm);
        return securityManager;
    }

    /**
     * 注册SessionManager,Session管理器
     *
     * @param sessionDAO session存储器
     *
     * @return SessionManager
     */
    @Bean
    public SessionManager sessionManager(SessionDAO sessionDAO) {
        ShiroSessionManager sessionManager = new ShiroSessionManager();
        sessionManager.setGlobalSessionTimeout(shiroPropriety.getTimeoutMillisecond());
        sessionManager.setSessionListeners(this.getSessionListeners());
        sessionManager.setSessionIdCookieEnabled(false);
        sessionManager.setSessionDAO(sessionDAO);
        return sessionManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
        shiroFilterFactory.setFilterChainDefinitionMap(this.getFilterChainDefinition());
        shiroFilterFactory.setSecurityManager(securityManager);
        shiroFilterFactory.setFilters(this.getFilterChain());
        return shiroFilterFactory;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisManager redisManager) {
        RedisCacheManager cacheManager = new RedisCacheManager();
        cacheManager.setPrincipalIdFieldName(SHIRO_PRINCIPAL_ID);
        cacheManager.setKeyPrefix(shiroPropriety.getKeyPrefix());
        cacheManager.setRedisManager(redisManager);
        return cacheManager;
    }

    /**
     * 开启Shiro AOP代码支持
     *
     * @return AuthorizationAttributeSourceAdvisor
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        attributeSourceAdvisor.setSecurityManager(securityManager);
        return attributeSourceAdvisor;
    }

    /**
     * 注册Shiro会话DAO
     *
     * @return SessionDAO
     */
    @Bean
    public SessionDAO sessionDAO(RedisManager redisManager, SessionIdGenerator sessionIdGenerator) {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setKeyPrefix(shiroPropriety.getSessionPrefix());
        sessionDAO.setSessionIdGenerator(sessionIdGenerator);
        sessionDAO.setRedisManager(redisManager);
        return sessionDAO;
    }

    /**
     * 注册Shiro密码凭证匹配器
     *
     * @return 凭证匹配器
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
        credentialsMatcher.setHashIterations(ITERATIONS);
        return credentialsMatcher;
    }

    @Bean
    public RedisManager redisManager() {
        return new RedisManager();
    }


    /*---------------------------------------------------私有方法------------------------------------------------------*/


    public RedisSerializer getRedisValueSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }

    /**
     * 获取全部的SessionListener
     *
     * @return SessionListeners
     */
    private Collection<SessionListener> getSessionListeners() {
        List<SessionListener> listeners = new ArrayList<>(2);
        listeners.add(shiroSessionListener);
        return listeners;
    }

    /**
     * 获取Shiro拦截器链
     *
     * @return Shiro拦截器链
     */
    private Map<String, Filter> getFilterChain() {
        Map<String, Filter> filterChain = new HashMap<>(2);
        filterChain.put(AUTHC, new ShiroAuthenticationFilter());
        return filterChain;
    }

    /**
     * 获取Shiro静态资源拦截器链
     *
     * @return Shiro静态资源拦截器链
     */
    private Map<String, String> getFilterChainDefinition() {
        Map<String, String> filterChainDefinition = new LinkedHashMap<>(2);
        filterChainDefinition.put("/approve/login", ANON);
        filterChainDefinition.put("/swagger-*/**", ANON);
        filterChainDefinition.put("/favicon.ico", ANON);
        filterChainDefinition.put("/doc.html/**", ANON);
        filterChainDefinition.put("/images/**", ANON);
        filterChainDefinition.put("/**/css/**", ANON);
        filterChainDefinition.put("/druid/**", ANON);
        filterChainDefinition.put("/**/js/**", ANON);
        filterChainDefinition.put("/v2/**", ANON);
        filterChainDefinition.put("/**", AUTHC);
        return filterChainDefinition;
    }
}
